/*
	MemChain.c

	memory block chain implementation

*/


/********************************************************************
 *
 * DHL_memchain.c - Utility for associating allocated memory
 *
 * Copyright 1997-2002 TeraLogic, Inc.
 * All Rights Reserved
 *
 * $Id: //suprahd/releases/suprahd_239/suprahd_zTVApp660_239/drivers/transport/DHL_memchain.c#1 $
 * $Revision: #1 $
 * $DateTime: 2006/06/05 14:53:58 $
 * $Change: 45890 $
 * $Author: catherine.lee $
 * 
 ********************************************************************/

#include "DHL_OSAL.h"
#include "DHL_memchain.h"




#define MEMCHAIN_MAGIC		0x45CBA9D1

/* structures */
typedef struct memElement {
	void*			userData;
	struct memElement	*next;
} memElement_t, *memElementPtr_t;
		
typedef struct memChainHead {
	UINT32		magic;
	UINT32		memLimit;
	myMalloc_f		Malloc;
	myFree_f		Free;
	UINT32		totalElements;
	UINT32		totalBytes;
	UINT32		totalUserBytes;
	memElementPtr_t	element;
} memChainHead_t, *memChainHeadPtr_t;

/*
	for the case when OS_Malloc or malloc is defined as macro.
*/
static void *memChainMalloc(unsigned size)
{
	return OS_Malloc(size);
}
static void memChainFree(void *p)
{
	// in fact, p is (void **) type. 
	// but for myFree_f, we use (void *).
	OS_Free(p);
}

/*=========================================================================
ErrCode memChainCreate (memId_t *memId, memChainSetupPtr_t setupPtr)

	*memId:		memId of the newly created memChain.
	setupPtr:		Pointer to the memChain setup information.

Creates a memChain object which links related memory into a 'chain'.  All
memory associated with this chain can then be easily freed.
=========================================================================*/
ErrCode memChainCreate (memId_t *memId, memChainSetupPtr_t setupPtr)
{
	myMalloc_f			Malloc;
	memChainHeadPtr_t		memChainHeadPtr;
	ErrCode			err = noError;


/*	OS_DbgPrintf("memChainCreate()\n"); */

	DHL_ASSERT((memId != NULL), "memChainCreate():param error");
	DHL_ASSERT((setupPtr != NULL), "memChainCreate():param error");
	if ((memId == NULL) || setupPtr == NULL) {
		err = DHL_FAIL_INVALID_PARAM;
		goto memChainCreateEnd;
	}

	if (setupPtr->Malloc == NULL) {
	    Malloc = memChainMalloc;
	}
	else {
		Malloc = setupPtr->Malloc;
	}
	memChainHeadPtr = (memChainHeadPtr_t)Malloc(sizeof(memChainHead_t));
	if (memChainHeadPtr == NULL) {
		err = DHL_FAIL_OUT_OF_MEMORY;
		goto memChainCreateEnd;
	}
	memset(memChainHeadPtr,0x00,sizeof(memChainHead_t));

	/* initialize */
	memChainHeadPtr->memLimit = setupPtr->memLimit;
	memChainHeadPtr->Malloc = Malloc;
	if (setupPtr->Free == NULL) {
		memChainHeadPtr->Free = memChainFree;
	}
	else {
		memChainHeadPtr->Free = setupPtr->Free;
	}
	memChainHeadPtr->magic = MEMCHAIN_MAGIC;
	memChainHeadPtr->totalElements = 0;
	memChainHeadPtr->totalBytes = sizeof(memChainHead_t);
	memChainHeadPtr->totalUserBytes = 0;
	memChainHeadPtr->element = NULL;

	/* set instance */
	*memId = memChainHeadPtr;

memChainCreateEnd:
	return (err);
}


/*=========================================================================
ErrCode memChainDestroy (memId_t memId)

	memId:		memId of the memChain.

Destroys the memChain, freeing all associated memory.
=========================================================================*/
ErrCode memChainDestroy (memId_t memId)
{
	memChainHeadPtr_t		memChainHeadPtr = (memChainHeadPtr_t)memId;
	memElementPtr_t		currElem, prevElem;


/*
	OS_DbgPrintf("memChainDestroy(%d)\n", instance);
	OS_DbgPrintf("totalElements=%d\n", memChainHeadPtr->totalElements);
*/
	DHL_ASSERT((memChainHeadPtr->magic == MEMCHAIN_MAGIC), "Not valid memChain.\n");
	memChainHeadPtr->magic = 0;

	/* free the memElements */
	currElem = memChainHeadPtr->element;
	while (currElem != NULL) {
		prevElem = currElem;
		currElem = currElem->next;
/*
		OS_DbgPrintf("free:0x%08X\n",(INT32)prevElem);
*/
		memChainHeadPtr->Free(&prevElem);
	}

	/* lastly, free the memChainHead */
	memChainHeadPtr->Free(&memChainHeadPtr);
/*	OS_DbgPrintf("free:0x%08X\n",memChainHeadPtr); */

	return (noError);		
}


/*=========================================================================
void* memChainAlloc (memId_t memId, UINT32 size)

	memId:		memId of the memChain.
	size:			The size of the requested memory chunk.

Allocates a memory block of the requested size and associates it with the
specified memChain.  Returns NULL if the OS_Calloc() fails or if memLimit
will be exceeded.
=========================================================================*/
void* memChainAlloc (memId_t memId, UINT32 size)
{
	memChainHeadPtr_t		memChainHeadPtr = (memChainHeadPtr_t)memId;
	memElementPtr_t		memElementPtr;
	UINT32			allocSize;
	void				*rptr, *p;


	DHL_ASSERT((memChainHeadPtr->magic == MEMCHAIN_MAGIC), "Not valid memChain.\n");

/*
	OS_DbgPrintf("memChainAlloc(%d), size:%d\n",instance,size);
*/
	/* check the memLimit (memLimit = 0 means no limit) */
	if (memChainHeadPtr->memLimit > 0) {
		if (memChainHeadPtr->totalUserBytes + size > memChainHeadPtr->memLimit) {
			OS_DbgPrintf("memLimit exceeded.  totalUserBytes=%d\n", memChainHeadPtr->totalUserBytes);
			rptr = NULL;
			goto memChainAllocEnd;
		}
	}

	/* allocate the memory - add 8 bytes to guarantee 64-bit alignment of user data */
	allocSize = sizeof(memElement_t) + size + 8;

	memElementPtr = (memElementPtr_t)memChainHeadPtr->Malloc(allocSize);
	if (memElementPtr == NULL) {
		rptr = NULL;
		goto memChainAllocEnd;
	}
	memset(memElementPtr,0x00,allocSize);

	p = (void *)(((UINT8 *)(memElementPtr) + sizeof(memElement_t)));

	/* align on 64-bit boundary */
	memElementPtr->userData = (void *)((UINT32)(((UINT8 *)p)+8) & ((UINT32)(~0) << 3)); /*!PRM typecase UINT32*/
	rptr = memElementPtr->userData;

	/* insert element in list */
	memElementPtr->next = memChainHeadPtr->element;
	memChainHeadPtr->element = memElementPtr;

	/* update tracking statistics */
	memChainHeadPtr->totalElements++;
	memChainHeadPtr->totalBytes += allocSize;
	memChainHeadPtr->totalUserBytes += size;

/*
	OS_DbgPrintf("\ttotalElements:%d,",memChainHeadPtr->totalElements);
	OS_DbgPrintf("totalBytes:%d,",memChainHeadPtr->totalBytes);
	OS_DbgPrintf("totalUserBytes:%d\n",memChainHeadPtr->totalUserBytes);
	OS_DbgPrintf("memElementPtr:0x%08X\n", (INT32)memElementPtr);
	OS_DbgPrintf("rptr:0x%08X\n",(INT32)rptr);
*/

memChainAllocEnd:
	return (rptr);
}


